#pragma once
#include <my_head.h>
#define LENGTH 4096 //每次内存复制的大小，必须是LENGTH的整数倍

int send_bigfile(int socketfd, char *name){
    int fd = open(name, O_RDWR);

    //获取要上传到服务端的文件的大小
    struct stat stat_file;
    fstat(fd, &stat_file);
    printf("file size: %ld\n", stat_file.st_size);
    
    //接收服务端现有文件的大小
    off_t real_size;
    recv(socketfd, &real_size, sizeof(off_t), MSG_WAITALL);
    printf("real size: %ld\n", real_size);
    
    //判断文件是否一致，一致则直接结束
    int flag_same = 0;
    if(real_size == stat_file.st_size){
        printf("文件完全一致，不需要上传\n");
        flag_same = 1;
        send(socketfd, &flag_same, sizeof(int), MSG_NOSIGNAL);
        return 0;
    }
    send(socketfd, &flag_same, sizeof(int), MSG_NOSIGNAL);
    
    //position用于记录当前文件指针的位置
    //因为position要作为mmap的offset参数，所以只能是LENGTH的整数倍
    int position = (real_size/LENGTH)*LENGTH;
    printf("position: %d\n", position);
    
    //每次传输的长度
    int length = LENGTH;
    
    //发送文件
    while(1){
        //如果总文件的大小和当前传输到的位置(positionon)相差的值小于一次传输的长度，就把长度改为这个差值
        if(stat_file.st_size - position < LENGTH){
            length = stat_file.st_size -position;
        }
        //从position的位置映射length的长度到file_read
        char *file_read = (char *)mmap(NULL, length, PROT_READ|PROT_WRITE, MAP_SHARED, fd, position);
        ERROR_CHECK(file_read, MAP_FAILED, "mmap");
        //传输内容的长度
        send(socketfd, &length, sizeof(int), MSG_NOSIGNAL);
        printf("length = %d\n", length);
        //传输的内容
        send(socketfd, file_read, length, MSG_NOSIGNAL);
        munmap(file_read, length);
        //传输一次后，更新position的值
        position += LENGTH;
        printf("position: %d\n", position);
        //如果position的值大于等于文件的总长度，说明position已经走到了文件的末尾
        //向服务端发送一个长度，值为0，表示传输结束
        if(position >= stat_file.st_size){
            length = 0;
            send(socketfd, &length, sizeof(int), MSG_NOSIGNAL);
            printf("文件传输完毕\n");
            break;
        }
    }
    close(fd);
    return 0;
}


int recv_bigfile(int socketfd, char *name){
    //根据服务端传来的信息判断要求的文件是否存在
    int flag;
    recv(socketfd, &flag, sizeof(flag), MSG_WAITALL);
    if(flag == -1){
        printf("要求的文件不存在\n");
        return 0;
    }
    
    //将文件下载到当前工作目录的download文件夹下
    chdir("download");
    int fd = open(name, O_WRONLY|O_CREAT, 0664);
    chdir("..");

    //现有文件大小
    struct stat stat_file;
    fstat(fd, &stat_file);
    printf("file size: %ld\n", stat_file.st_size);
    //将现有文件大小发给客户端
    send(socketfd, &stat_file.st_size, sizeof(off_t), MSG_NOSIGNAL);
    
    //判断文件是否一致，一致则直接结束
    int flag_same;
    recv(socketfd, &flag_same, sizeof(int), MSG_WAITALL);
    if(flag_same == 1){
        printf("文件完全一致，不需要更新\n");
        return 0;
    }

    //获取断点
    off_t position = (stat_file.st_size/LENGTH)*LENGTH;
    printf("position: %ld\n", position);
    //改变文件指针的指向到断点
    int ret_lseek = lseek(fd, position, SEEK_SET);
    ERROR_CHECK(ret_lseek, -1, "lseek");
    
    //接收文件
    while(1){
        int buf_len;
        char buf[LENGTH+1] = {0};
        recv(socketfd, &buf_len, sizeof(int), MSG_WAITALL);
        //printf("buf_len: %d\n", buf_len);
        if(buf_len == 0){
            printf("文件下载完毕\n");
            break;
        }
        recv(socketfd, buf, buf_len, MSG_WAITALL);
        //printf("buf: %s\n", buf);
        write(fd, buf, buf_len);
    }
    close(fd);
    return 0;
}

void login_in(int socketfd){
    //验证登录
    int count_error = 0;
    while(1){
        //存储用户名和密码
        char uname[20] = {0};
        char password[20] = {0};
        //用户在客户端输入用户名和密码
        printf("用户名:");
        scanf("%s", uname);
        printf("密码:");
        scanf("%s", password);
        //客户端发送用户名和密码到服务端
        send(socketfd, uname, sizeof(uname), MSG_NOSIGNAL);
        send(socketfd, password, sizeof(password), MSG_NOSIGNAL);

        //接收服务端发回的标记判断能否登录
        int flag_login;
        recv(socketfd, &flag_login, sizeof(int), MSG_WAITALL);
        if(flag_login == 0){
            printf("登录成功！\n");
            break;
        }
        else if(flag_login == 1){
            printf("没有该用户！\n");
        }
        else if(flag_login == 2){
            printf("密码错误！\n");
            count_error += 1;
        }
        if(count_error == 3){
            printf("连续三次输入密码错误！拒绝访问！\n");
            close(socketfd);
            exit(1);
        }
    }
}
